/*
 * KernelBasisComponent.java
 *
 * Created on April 27, 2005, 2:17 PM
 */

package com.milowski.monos.component;

import java.util.*;

import com.milowski.monos.*;
import com.milowski.monos.binomial.*;
import com.milowski.monos.monomial.*;
import java.util.logging.Logger;
import org.infoset.component.Component;
import org.infoset.component.ItemFilterComponent;
import org.infoset.xml.Document;
import org.infoset.xml.Element;
import org.infoset.xml.InfosetFactory;
import org.infoset.xml.ItemConstructor;
import org.infoset.xml.ItemDestination;
import org.infoset.xml.Name;
import org.infoset.xml.XMLException;
import org.infoset.xml.filter.SubtreeFilter;

/**
 *
 * @author R. Alexander Milowski
 */
public class BinomialGroebnerBasisComponent extends MonosComponentBase
{
   
   static final Name componentName = InfosetFactory.createName(XML.STEPS_NAMESPACE,"binomial-groebner");
   static Name [] subtreeNames = { XML.computeBasisName, XML.computeToricIdealName };
   
   public static class BinomialGroebnerBasisFilter extends SubtreeFilter {
      int varMax;
      int generatorMax;
      Logger log;
      
      BinomialGroebnerBasisFilter(Logger log,int varMax, int generatorMax) {
         super(subtreeNames);
         this.log = log;
         this.varMax = varMax;
         this.generatorMax = generatorMax;
      }
      public void process(Document doc) 
         throws XMLException
      {
         Element top = doc.getDocumentElement();
         boolean toric = top.getName().equals(XML.computeToricIdealName);
         String monomialOrderSpec = top.getAttributeValue("monomial-order");
         String outputRequested = top.getAttributeValue("output");
         GroebnerBasis.Ordering ordering = GroebnerBasis.DEGREE_REVERSE_LEXICOGRAPHIC;
         if (monomialOrderSpec!=null) {
            if (monomialOrderSpec.equals("lex")) {
               ordering = GroebnerBasis.LEXICOGRAPHIC;
            } else if (monomialOrderSpec.equals("revlex")) {
               ordering = GroebnerBasis.REVERSE_LEXICOGRAPHIC;
            } else if (monomialOrderSpec.equals("grevlex")) {
               ordering = GroebnerBasis.DEGREE_REVERSE_LEXICOGRAPHIC;
            } else if (monomialOrderSpec.equals("wgrevlex")) {
               Iterator<Element> matches = top.getElementsByName(XML.weightName);
               Element representation = matches.hasNext() ? matches.next() : null;
               if (representation==null) {
                  throw new XMLException("Missing "+XML.weightName+" element.");
               }
               Monomial m = (Monomial)XML.unmarshall(representation);
               ordering = new GroebnerBasis.WeightedReverseLexicographicOrdering(m.getValues());
            } else {
               throw new XMLException("Unknown monomial ordering: "+monomialOrderSpec);
            }
         }
         Iterator<Element> children = top.getElementChildren();
         if (!children.hasNext()) {
            throw new XMLException("The "+XML.computeBasisName+" element requires a matrix child.");
         }
         GroebnerBasis gbasis = null;
         if (toric) {
            Element matrixElement = (Element)children.next();
            MatrixBasis kernel = MatrixBasis.fromXML(matrixElement);
            if (varMax>0 && kernel.getColumnCount()>varMax) {
               XML.error(InfosetFactory.getDefaultInfoset().createItemConstructor(),output,"The number of variables is too large: "+kernel.getColumnCount()+" > "+varMax);
               return;
            }
            BinomialIdeal ideal = kernel.toBinomials(false);
            gbasis = new GroebnerBasis(ideal.getLexOrder());
            gbasis.setMonomialOrdering(ordering);
            gbasis.setLog(log);
            Iterator<Element> rowVectors = top.getElementsByName(XML.rowSpaceVectorName);
            Element representation = rowVectors.hasNext() ? rowVectors.next() : null;
            if (representation==null) {
               throw new XMLException("The positive row space vector element is missing.");
            }
            Monomial rowVector = Monomial.fromXML(representation);
            Iterator<Element> reorders = top.getElementsByName(XML.reorderName);
            representation = reorders.hasNext() ? reorders.next() : null;
            if (representation!=null) {
               Monomial r = Monomial.fromXML(representation);
               short [] values = r.getValues();
               if (values.length!=ideal.getLexOrder().size()) {
                  throw new XMLException("The reorder is not the same size as the lexicographic ordering.");
               }
               int [] reorder = new int[values.length];
               for (int i=0; i<reorder.length; i++) {
                  reorder[i] = values[i];
               }
               gbasis.setExponentReOrdering(reorder);
            }
            log.fine("Computing toric ideal groebner basis...");
            try {
               gbasis.computeToricIdeal(ideal, rowVector.getValues());
            } catch (Exception ex) {
               throw new XMLException("Computation failed: "+ex.getMessage(),ex);
            }
         } else {
            Element idealElement = (Element)children.next();
            BinomialIdeal ideal = BinomialIdeal.fromXML(idealElement,true);
            if (varMax>0 && ideal.getLexOrder().size()>varMax) {
               XML.error(InfosetFactory.getDefaultInfoset().createItemConstructor(),output,"The number of variables is too large: "+ideal.getLexOrder().size()+" > "+varMax);
               return;
            }
            gbasis = new GroebnerBasis(ideal.getLexOrder());
            gbasis.setMonomialOrdering(ordering);
            gbasis.setLog(log);
            log.fine("Computing binomial ideal groebner basis...");
            try {
               gbasis.computeFullBasis(ideal);
            } catch (Exception ex) {
               throw new XMLException("Computation failed: "+ex.getMessage(),ex);
            }
         }

         log.fine("Finished basis.");
         BinomialIdeal basisOutput = new BinomialIdeal(gbasis.getLexOrder());
         boolean compact = outputRequested==null ? toric : (outputRequested.equals("compact") || outputRequested.equals("matrix-compact"));
         boolean matrix = outputRequested==null ? toric : outputRequested.startsWith("matrix");
         // TODO: shouldn't have to convert this
         for (Iterator basisElements = gbasis.getElements(); basisElements.hasNext(); ) {
            Binomial b = (Binomial)basisElements.next();
            basisOutput.add(b);
         }
         ItemConstructor constructor = InfosetFactory.getDefaultInfoset().createItemConstructor(doc.getBaseURI());
         basisOutput.toXML(matrix,compact,constructor, output);
      }
   }
   
   /** Creates a new instance of KernelBasisComponent */
   public BinomialGroebnerBasisComponent()
   {
      super(componentName);
   }
   
   public Component newInstance() 
      throws XMLException
   {
      final BinomialGroebnerBasisFilter filter = new BinomialGroebnerBasisFilter(Logger.getAnonymousLogger(),0,0);
      return new ItemFilterComponent(name,vendor,version,filter,inputPortName,outputPortName) 
      {
         public void init(Component.Context context) 
            throws XMLException
         {
            filter.log = context.getLog();
            Object o = context.getParameter(InfosetFactory.createName("variable-max"));
            filter.varMax = o==null ? 0 : Integer.parseInt(o.toString());
            o = context.getParameter(InfosetFactory.createName("generator-max"));
            filter.generatorMax= o==null ? 0 : Integer.parseInt(o.toString());
         }
      };
   }
   
}
